/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import { noop } from '@baize/wdk';

import { isHvigorLogWithTime } from '../../base/util/time-util';

export type UnHook = () => void;

export type Transformer = (output: string) => boolean;

export class StdHook {
  promise: Promise<void>;
  unhook: UnHook;

  constructor(p: Promise<void>, u: UnHook) {
    this.promise = p;
    this.unhook = u;
  }
}

const hook = (stream: (NodeJS.WriteStream & { fd: 1 }) | (NodeJS.WriteStream & { fd: 2 }), transformer: Transformer) => {
  const { write } = stream;
  let unHook: UnHook = noop;
  return new StdHook(
    new Promise((resolve) => {
      unHook = () => {
        stream.write = write;
        resolve();
      };
      stream.write = (output: string) => {
        return transformer(String(output));
      };
    }),
    unHook,
  );
};

function stdOutHook(transformer: Transformer): StdHook {
  return hook(process.stdout, transformer);
}

function stdErrHook(transformer: Transformer): StdHook {
  return hook(process.stderr, transformer);
}

const timePrefixLength = 26;
const deprecationWarning = 'DeprecationWarning:';

let outHook: StdHook;
let errHook: StdHook;

/**
 * 获取stdout的hook
 */
function getOutHook(): StdHook {
  return stdOutHook((output: string) => {
    outHook.unhook();
    write(output, true);
    outHook = getOutHook();
    return true;
  });
}

/**
 * 获取stderr的hook
 */
function getErrHook(): StdHook {
  return stdErrHook((output: string) => {
    errHook.unhook();
    write(output);
    errHook = getErrHook();
    return true;
  });
}

/**
 * 定制化的流的写操作
 *
 * @param output 需要输出的字符串
 * @param isOutHook 是否是标准输出流
 */
function write(output: string, isOutHook?: boolean): void {
  const stream = isOutHook ? process.stdout : process.stderr;
  if (!output.includes(deprecationWarning)) {
    if (isHvigorLogWithTime(output)) {
      stream.write(output.substring(timePrefixLength));
    } else {
      stream.write(output);
    }
  }
}

/**
 * 初始化stdout和stderr的hook
 */
export function initHook(): void {
  outHook = getOutHook();
  errHook = getErrHook();
}

/**
 * 取消stdout和stderr的hook
 */
export function unhook(): void {
  outHook.unhook();
  errHook.unhook();
}
